#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cbb_encoder_process.h"
#include "cbb_encoder_convert.h"


hd_s32_t EncoderGetFrameSize(CbbEncoderHandle_t *p_encoder_handle)
{
    if (NULL == p_encoder_handle) {
        return CBB_CAMERA_ACK_ERROR;
    }
    hd_u32_t frame_size = 0;
    switch (p_encoder_handle->format)
    {
    case MPP_FMT_YUV420SP:
    case MPP_FMT_YUV420SP_VU:
        frame_size = MPP_ALIGN(p_encoder_handle->hor_stride, 64) * MPP_ALIGN(p_encoder_handle->ver_stride , 64) * 3 / 2;
        break;
    case MPP_FMT_YUV422SP:
    case MPP_FMT_YUV422SP_VU:
    case MPP_FMT_YUV422_YUYV:
    case MPP_FMT_YUV422_YVYU:
    case MPP_FMT_YUV422_UYVY:
    case MPP_FMT_YUV422_VYUY:
        frame_size = MPP_ALIGN(p_encoder_handle->hor_stride, 64) * MPP_ALIGN(p_encoder_handle->ver_stride , 64) * 2;
        break;
    default:
        break;
    }

    return frame_size;
}

EncoderHandle CbbEncoderInit(CbbEncoderConf_t enc_conf)
{   
    CbbEncoderHandle_t *p_encoder_handle = NULL;
    p_encoder_handle = calloc(1, sizeof(CbbEncoderHandle_t));
    if (!p_encoder_handle) {
        printf("calloc p_encoder_handle failed\r\n");
        return NULL;
    }
    p_encoder_handle->channnel = enc_conf.channel;
    p_encoder_handle->width = enc_conf.width;
    p_encoder_handle->height = enc_conf.height;
    //水平垂直分布参数
    p_encoder_handle->hor_stride = MPP_ALIGN(enc_conf.width, 16);
    p_encoder_handle->ver_stride = MPP_ALIGN(enc_conf.height, 16);

    int ret = -1;
    do
    {
        //转换图像格式
        ret = CbbConvertEncoderFormat(enc_conf.in_format);
        if (ret < 0) {
            printf("CbbConvertEncoderFormat:%d failed\r\n", enc_conf.in_format);
            return NULL;
        }
        p_encoder_handle->format = ret;

        //转换编码类型
        ret = CbbConvertEncoderType(enc_conf.out_format);
        if (ret < 0) {
            printf("CbbConvertEncoderType:%d failed\r\n", enc_conf.out_format);
            return NULL;
        }
        p_encoder_handle->type = ret;
        //计算frame_size
        p_encoder_handle->frame_size = EncoderGetFrameSize(p_encoder_handle);
        printf("p_encoder_handle->frame_size = %d\r\n", p_encoder_handle->frame_size);
        if (p_encoder_handle->frame_size <= 0) {
            printf("get frame_size failed\r\n");
            return NULL;
        }

        //申请组
        ret = mpp_buffer_group_get_internal(&p_encoder_handle->buf_group, MPP_BUFFER_TYPE_DRM | MPP_BUFFER_FLAGS_CACHABLE);
        if (ret < 0) {
            printf("mpp_buffer_group_get_internal failed\r\n");
            break;;
        }
        //申请frame buffer
        ret = mpp_buffer_get(p_encoder_handle->buf_group, &p_encoder_handle->frame_buffer, p_encoder_handle->frame_size);
        if (ret < 0) {
            printf("mpp_buffer_get frame_buffer failed\r\n");
            break;;
        }
        //申请packet buffer
        ret = mpp_buffer_get(p_encoder_handle->buf_group, &p_encoder_handle->packet_buffer, p_encoder_handle->frame_size);
        if (ret < 0) {
            printf("mpp_buffer_get packet_buffer failed\r\n");
            break;;
        }

        //创建ctx
        ret = mpp_create(&p_encoder_handle->ctx, &p_encoder_handle->mpi);
        if (ret < 0) {
            printf("mpp_create failed. ret = %d\r\n", ret);
            break;;
        }

        //初始化mpi
        ret = mpp_init(p_encoder_handle->ctx, MPP_CTX_ENC, p_encoder_handle->type); //编码格式需要转换
        if (ret < 0) {
            printf("mpp_init failed. ret = %d\r\n", ret);
            break;;
        }

        //设置编码器参数
        ret = CbbSetEncoderConf(p_encoder_handle, enc_conf);
        if (ret < 0) {
            printf("mpp_init failed. ret = %d\r\n", ret);
            break;;
        }
        //到这一步表示初始化完成,直接返回
        return p_encoder_handle;

    } while (0);
    

    return NULL;
}

CbbCameraAckE_t CbbEncoderRun(EncoderHandle handle , const CbbCameraFrame_t raw_frame, CbbEncoderRes_t *enc_res)
{
    if (NULL == handle || NULL == enc_res || NULL == raw_frame.data_addr) {
        printf("Invalid Params\r\n");
        return CBB_CAMERA_ACK_ERROR;
    }
    CbbEncoderHandle_t *p_encoder_handle  = (CbbEncoderHandle_t *)handle;

    void *frame_buf = mpp_buffer_get_ptr(p_encoder_handle->frame_buffer);
    memcpy(frame_buf, raw_frame.data_addr, raw_frame.len);//不管如何都进行一次拷贝  需要考虑零拷贝输入的方式
    // frame_buf = raw_frame.data_addr;
    //帧结束标志
    p_encoder_handle->frm_eos = 1;
    //申请帧缓存
    MppFrame frame = NULL;
    //申请输出码流缓存
    MppFrame packet = NULL;
    //初始化帧缓存
    int ret = mpp_frame_init(&frame);
    if (ret){
        printf("mpp_frame_init failed\r\n");
        return CBB_CAMERA_ACK_ERROR;
    }
    //设置帧缓存信息
    //宽
    mpp_frame_set_width(frame, p_encoder_handle->width);
    //高
    mpp_frame_set_height(frame, p_encoder_handle->height);
    //水平分布
    mpp_frame_set_hor_stride(frame, p_encoder_handle->hor_stride);
    //垂直分布
    mpp_frame_set_ver_stride(frame, p_encoder_handle->ver_stride);
    //帧格式
    mpp_frame_set_fmt(frame, p_encoder_handle->format); //格式需要转换
    //帧buffer
    mpp_frame_set_buffer(frame, p_encoder_handle->frame_buffer);
    //帧结束标志
    mpp_frame_set_eos(frame, p_encoder_handle->frm_eos);
    //初始化packet buffer
    mpp_packet_init_with_buffer(&packet, p_encoder_handle->packet_buffer);
    //初始化packet长度 这一步很重要!!!
    mpp_packet_set_length(packet, 0);
    
    //输入编码原始帧数据
    ret = p_encoder_handle->mpi->encode_put_frame(p_encoder_handle->ctx, frame);
    if (ret){
        printf("encode_put_frame failed, ret = %d\r\n", ret);
        return CBB_CAMERA_ACK_ERROR;
    }
    //释放申请的帧缓存
    mpp_frame_deinit(&frame);
    
    //获取编码后的数据
    ret = p_encoder_handle->mpi->encode_get_packet(p_encoder_handle->ctx, &packet);
    if (ret){
        printf("encode_get_packet failed, ret = %d\r\n", ret);
        return CBB_CAMERA_ACK_ERROR;
    }
    //编码成功
    if (packet)
    {
        //获取编码后数据长度
        enc_res->len = mpp_packet_get_length(packet);
        printf("mpp_packet_get_length:%d\r\n", enc_res->len);
        //获取编码结束标志
        p_encoder_handle->pkt_eos = mpp_packet_get_eos(packet);
        //获取编码数据起始指针
        void *ptr = mpp_packet_get_pos(packet);
        //编码后数据
        memcpy(enc_res->res_data, ptr, enc_res->len);
        //帧ID计数
        enc_res->index = raw_frame.index;
        //编码格式
        enc_res->out_format = p_encoder_handle->type;
        //编码耗费时间

        
        mpp_packet_deinit(&packet);
        return CBB_CAMERA_ACK_OK;
    }
    
    return CBB_CAMERA_ACK_ERROR;
}

CbbCameraAckE_t CbbEncoderCtrl(EncoderHandle handle,const CbbEncoderActionE_t action,CbbEncoderCtrlDev_t  *operate_p)
{

}

CbbCameraAckE_t CbbEncoderDestroy(EncoderHandle handle)
{
    if (NULL == handle) {
        return CBB_CAMERA_ACK_ERROR;
    }

    CbbEncoderHandle_t *p_encoder_handle  = (CbbEncoderHandle_t *)handle;
    
    printf("mpp_destroy ctx\r\n");
    if (p_encoder_handle->ctx) {
        mpp_destroy(p_encoder_handle->ctx);
        p_encoder_handle->ctx = NULL;
    }

    printf("mpp_destroy frame_buffer\r\n");
    if (p_encoder_handle->frame_buffer) {
        mpp_buffer_put(p_encoder_handle->frame_buffer);
        p_encoder_handle->frame_buffer = NULL;
    }

    printf("mpp_destroy packet_buffer\r\n");
    if (p_encoder_handle->packet_buffer) {
        mpp_buffer_put(p_encoder_handle->packet_buffer);
        p_encoder_handle->packet_buffer = NULL;
    }

    printf("mpp_destroy buf_group\r\n");

    if (p_encoder_handle->buf_group) {
        mpp_buffer_group_put(p_encoder_handle->buf_group);
        p_encoder_handle->buf_group = NULL;
    }

    printf("free p_encoder_handle \r\n");
    free(p_encoder_handle);

    return CBB_CAMERA_ACK_OK;
}

void CbbEncoderVersioin(CbbVersion_t *version_p)
{

}


CbbCameraAckE_t CbbSetEncoderConf(CbbEncoderHandle_t *encoder_handle, CbbEncoderConf_t enc_conf)
{
    if (NULL == encoder_handle) {
        return CBB_CAMERA_ACK_ERROR;
    }
    
    //设置编码器参数
    MppEncCfg cfg;
    int ret = mpp_enc_cfg_init(&cfg);
    if (ret != MPP_OK) {
        printf("mpp_enc_cfg_init failed. ret = %d\r\n", ret);
        return -1;
    }

    ret = encoder_handle->mpi->control(encoder_handle->ctx, MPP_ENC_GET_CFG, cfg);
    if (ret) {
        printf("get enc cfg failed ret %d\n", ret);
        return -1;
    }
    mpp_enc_cfg_set_s32(cfg, "tune:scene_mode", 0); //0-defalut 1-ipc
    mpp_enc_cfg_set_s32(cfg, "prep:width", enc_conf.width);
    mpp_enc_cfg_set_s32(cfg, "prep:height", enc_conf.height);
    mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", MPP_ALIGN(enc_conf.width, 16));
    mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", MPP_ALIGN(enc_conf.height, 16));
    mpp_enc_cfg_set_s32(cfg, "prep:format", encoder_handle->format);

    RK_S32 rc_mode = MPP_ENC_RC_MODE_BUTT;
    if (rc_mode == MPP_ENC_RC_MODE_BUTT)
        rc_mode = (encoder_handle->format == MPP_VIDEO_CodingMJPEG) ? MPP_ENC_RC_MODE_FIXQP : MPP_ENC_RC_MODE_VBR; //格式需要转换
    mpp_enc_cfg_set_s32(cfg, "rc:mode", rc_mode);

    /* fix input / output frame rate */
    RK_S32 fps_in_den = 0, fps_in_num = 0, fps_out_den = 0, fps_out_num = 0;
    if (fps_in_den == 0)
        fps_in_den = 1;
    if (fps_in_num == 0)
        fps_in_num = 30;
    if (fps_out_den == 0)
        fps_out_den = 1;
    if (fps_out_num == 0)
        fps_out_num = 30;
    RK_S32 bps = enc_conf.width * enc_conf.height / 8 * (fps_out_num / fps_out_den);

    //固定码率
    if (rc_mode == MPP_ENC_RC_MODE_VBR) {
        mpp_enc_cfg_set_s32(cfg, "rc:bps_max", bps * 17 / 16);
        mpp_enc_cfg_set_s32(cfg, "rc:bps_min", bps * 1 / 16);
    }
    if (encoder_handle->type == MPP_VIDEO_CodingAVC) {
        RK_U32 constraint_set;
        /*
         * H.264 profile_idc parameter
         * 66  - Baseline profile
         * 77  - Main profile
         * 100 - High profile
         */
        mpp_enc_cfg_set_s32(cfg, "h264:profile", 100);
        /*
         * H.264 level_idc parameter
         * 10 / 11 / 12 / 13    - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
         * 20 / 21 / 22         - cif@30fps / half-D1@@25fps / D1@12.5fps
         * 30 / 31 / 32         - D1@25fps / 720p@30fps / 720p@60fps
         * 40 / 41 / 42         - 1080p@30fps / 1080p@30fps / 1080p@60fps
         * 50 / 51 / 52         - 4K@30fps
         */
        mpp_enc_cfg_set_s32(cfg, "h264:level", 40);
        mpp_enc_cfg_set_s32(cfg, "h264:cabac_en", 1);
        mpp_enc_cfg_set_s32(cfg, "h264:cabac_idc", 0);
        mpp_enc_cfg_set_s32(cfg, "h264:trans8x8", 1);

        // mpp_env_get_u32("constraint_set", &constraint_set, 0);
        // if (constraint_set & 0x3f0000)
        //     mpp_enc_cfg_set_s32(cfg, "h264:constraint_set", constraint_set);
    }


    mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", 0);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", fps_in_num);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denom", fps_in_den);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", 0);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", fps_out_num);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denom", fps_out_den);

    /* drop frame or not when bitrate overflow */
    mpp_enc_cfg_set_u32(cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED);
    mpp_enc_cfg_set_u32(cfg, "rc:drop_thd", 20);        /* 20% of max bps */
    mpp_enc_cfg_set_u32(cfg, "rc:drop_gap", 1);         /* Do not continuous drop frame */

    /* setup bitrate for different rc_mode */
    mpp_enc_cfg_set_s32(cfg, "rc:bps_target", bps);
    /* jpeg use special codec config to control qtable */
    mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor",  80);
    mpp_enc_cfg_set_s32(cfg, "jpeg:qf_max",  99);
    //
    mpp_enc_cfg_set_s32(cfg, "jpeg:qf_min",  1);
    //翻转 镜像  角度  
    mpp_enc_cfg_set_s32(cfg, "prep:mirroring", 0);
    mpp_enc_cfg_set_s32(cfg, "prep:rotation", enc_conf.rotation);
    mpp_enc_cfg_set_s32(cfg, "prep:flip", 0);
    //编码类型
    mpp_enc_cfg_set_s32(cfg, "codec:type", encoder_handle->type);

    mpp_enc_cfg_set_s32(cfg, "rc:gop", fps_out_num * 2);

    ret = encoder_handle->mpi->control(encoder_handle->ctx, MPP_ENC_SET_CFG, cfg);
    if (ret < 0) {
        printf("MPP_ENC_SET_CFG failed\r\n");
        return CBB_CAMERA_ACK_ERROR;
    }

    return CBB_CAMERA_ACK_OK;
}
